import { ERR_OK, ERR_FALSE }  from '@/assets/js/config.js'
import { DEBUG } from '@/store/config.js'
import utils from '@/assets/js/util.js'
import request from '@/uni_modules/yb-request/js_sdk'
import gb2312 from '@/assets/other/gb2312.js'
import cryptoJS from "@/assets/crypto-js/crypto-js.js"
import base64 from "@/assets/other/base64.js"
import htmlParser from "@/uni_modules/html-parser/js_sdk/index.js"

const { urlPrams, dateFormat, randomId, deepClone, typeCall, deleteProperty } = utils
const Utils = utils//工具栏
const HTMLParser = htmlParser//格式化html字符串，用于获取数据
const Base64 = base64//base64加密/解密方法
const CryptoJS = cryptoJS//cryptoJS加密
const Xhr = request.xhr//请求方法
const Http = request.http//请求方法
const GBK = gb2312//字符串转gbk
export const statement = [
	'@sync:',
	'@async:',
	'@sel:',
	'@json:',
	'@for:',
	'@map:',
	'@filter:',
	'@find:',
	'@sort:',
	'@unique:',
	'@replace:',
	'@replaceAll:',
	'@plain:',
	'@split:',
	'@string:',
	'@dateFormat:',
	'@join:',
	'@slice:',
	'@trim:',
	'@have:',
	'@unhave:',
	'@equal:',
	'@unequal:',
	'@eval:',
	'@query:',
	'@match:',
	'@reverse:',
	'@encodeUri:',
	'@decodeUri:',
	'@objectKeys:',
	'@objectArray:',
	'@upper:',
	'@lower:',
	'@response:',
	'@console:'
]//声明符

export default class Network {
	constructor (source) {
		this.source = source//源
		this.baseUrl = source.href//基础域名
		this.taskIds = []//任务ID列表
		this.responses = []//请求结果缓存
		this.logs = []//日志列表
		this.nets = []//网络请求记录
		this.formatLimiter = new ConcurrentLimiter(3)// 最多同时执行3个
	}
	//销毁源
	destroy () {
		if ( this.taskIds.length > 0 ) {
			request.http.abort(this.taskIds.toString())
			request.xhr.abort(this.taskIds.toString())
			webviewTaskabort(this.taskIds)
		}
		//关闭webworker
		if ( ww ) plus.webview.close(ww)
		this.formatLimiter.clear()
		this.formatLimiter = null
		this.timestamp = 0
		this.baseUrl = ''
		this.taskIds = []
		this.responses = []
		this.logs = []
		this.nets = []
	}
	//加入日志
	appendLog (options) {
		const debug = uni.getStorageSync(DEBUG)
		if ( !debug ) return
		this.logs.push(Object.assign({}, {
			message: '',
			type: 'info',
			result: undefined,
		}, options, {
			by: 'global.js:' + (options.by || 'unknow'),
		}))
	}
	//清空日志
	clearLog () {
		this.logs = []
	}
	//加入请求记录
	appendNet (options) {
		const debug = uni.getStorageSync(DEBUG)
		if ( !debug ) return
		const newTime = new Date().getTime()
		const index = this.nets.findIndex(net => urlPrams(net.url, net.params) == urlPrams(options.url, options.params) && options.isWebview == net.isWebview && newTime - net.timestamp < 5000)
		if ( index == -1 ) {
			const search = options.url.split('?')[1] || ''
			const arr = search ? search.split('&') : []
			const query = {}
			arr.forEach(item => {
				const arg = item.split('=')
				query[arg[0]] = arg[1]
			})
			this.nets.push(
				Object.assign({}, {
					url: '',
					type: 'http',
					method: 'get',
					query,
					header: {},
					status: 200,
					response: '',
					charset: 'utf-8',
					isCache: false,
					isWebview: false,
					timestamp: new Date().getTime()
				}, options)
			)
		}
	}
	//清空请求记录
	clearNet () {
		this.nets = []
	}
	//初始化
	async init () {
		//如果定义了应用首次启动执行方法，需要执行该方法，考虑到可能需要获取异步数据初始化一些变量
		if ( this.source.onLaunch ) await this.$text(this.source.onLaunch, null, null)
	}
	//请求
	$request ({url, params, params1, header, method, webview = 0, delay, visits, timeout, sslVerify, device, charset, response, responseType, cacheEfficiency}) {
		return new Promise(async (resolve, reject) => {
			let status, resolveContent, isCache
			const globalConfig = this.source.globalConfig || {}
			const timestamp = typeof cacheEfficiency == 'number' ? cacheEfficiency : typeof globalConfig.cacheEfficiency == 'number' ? globalConfig.cacheEfficiency : 10000
			this.responses = this.responses.filter(rep => new Date().getTime() - rep.timestamp < timestamp)//清空超过有效时间的缓存
			const index = timestamp == 0 ? -1 : this.responses.findIndex(rep => (rep.url || '') == urlPrams(url || '', params || {}) && rep.webview == webview)
			const taskId = index > -1 ? this.responses[index].taskId : randomId()
			if ( index > -1 ) {
				const content = this.responses[index].content
				const originContent = this.responses[index].originContent
				const responseRule = this.responses[index].responseRule
				resolve({
					taskId,
					//如果传入了结果处理规则并且和缓存的处理规则不同，则重新处理原始结果，否则直接返回缓存的处理结果
					data: response && response != responseRule ? await this.$text(response, originContent, taskId) : content
				})
				status = 200
				isCache = true
				resolveContent = content
			} else {
				this.taskIds.push(taskId)
				const requestUrl = url || ''
				const baseUrl = url != '...' ? requestUrl.slice(0, 4) == 'http' ? '' : await this.getBaseUrl() : ''
				const config = {
					requestUrl: baseUrl + requestUrl,//请求链接
					requestParams: params || {},//
					requestHeader: header || {},
					requestMethod: method || 'get',
					requestCharset: charset || '',
					requestDevice: device || '电脑',
					requestTimeout: timeout || globalConfig.timeout || 10000,//请求超时时间默认为10秒
					requestDelay: typeof delay == 'number' ? delay : 1000,//webview请求延迟多少秒去获取内容（默认为1秒）
					requestVisits: visits || 1//webview请求访问多少次后去获取内容，因为一些网址可能会中转多次（默认为1次）
				}
				const realheader = Object.assign({}, {
					Referer: baseUrl
				}, config.requestHeader, {
					Charset: config.requestCharset == 'GBK' ? 'gbk' : 'utf-8'
				})
				realheader[config.requestCharset == 'GBK' ? 'UserAgent' : 'user-agent'] = config.requestDevice == '手机' ? 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
				try{
					let res = {}, content, newContent
					//如果url为...，并且传入了response，表示不请求，直接通过response规则返回内容
					if ( url == '...' && response ) {
						content = await this.$text(response, null, null)
						newContent = content
					} else {
						res = webview ? await getWebviewContent(config, realheader, taskId) : await request[config.requestCharset == 'GBK' ? 'xhr' : 'http'][config.requestMethod.toLowerCase()](config.requestUrl, {
							taskId: taskId,
							params: config.requestParams,
							timeout: config.requestTimeout,
							mimeType: config.requestHeader.mimeType || (config.requestCharset == 'GBK' ? 'text/html;charset=gb2312' : undefined),
							header: realheader,
							responseType,
							sslVerify: sslVerify == 1 ? true : false
						})
						content = responseType == 'arraybuffer' ? uni.arrayBufferToBase64(res.data) : typeof res.data == 'string' ? htmlDecode(res.data) : res.data
						newContent = response ? await this.$text(response, content, taskId) : content
					}
					this.taskIds = this.taskIds.filter(id => id != taskId)
					//记录缓存
					this.responses.push({
						taskId: taskId,
						timestamp: new Date().getTime(),
						url: urlPrams(url || '', params || {}),
						header: res.header || {},
						content: newContent,//记录处理后的内容
						originContent: content,//记录原始内容
						responseRule: response,//记录结果处理规则
						params: params1 || {},//记录连接参数
						webview
					})
					resolve({
						taskId,
						data: newContent
					})
					status = 200
					isCache = false
					resolveContent = content
				}catch(err){
					this.taskIds = this.taskIds.filter(id => id != taskId)
					reject(err)
					status = 500
					isCache = false
					resolveContent = err
				}
			}
			this.appendNet({
				url: url || '/',
				method: method || 'get',
				params,
				header: header || {},
				charset: charset || 'utf-8',
				type: charset == 'gbk' ? 'xhr' : 'http',
				status: status,
				response: resolveContent,
				isCache: isCache,
				isWebview: webview == 1
			})
		})
	}
	//获取规则前缀
	$pre (rule) {
		return rule.indexOf(':') > -1 ? rule.slice(0, rule.indexOf(':')+1) : ''
	}
	//正则匹配源规则属性
	$extract (inputString, patterns) {
		try{
			return extractPattern(inputString, patterns)
		}catch(e){
			return null
		}
	}
	//处理规则列表
	async $list (resource, content, taskId) {
		const list = resource.list ? await this.$text(resource.list, content, taskId) : [content]
		if ( !list ) return resource.list ? [] : null//如果获取list列表失败，直接返回空值
		const index = this.responses.findIndex(re => re.taskId == taskId)//获取是否有对应任务ID的缓存
		if ( resource.filter ) {
			for ( let i = 0; i < list.length; i++ ) {
				//将序号索引存入缓存responses中，方便调用
				if ( this.responses[index] ) this.responses[index].index = i
				const value = await this.$text(resource.filter, list[i], taskId)
				if ( !value ) list.splice(i, 1)
			}
		}
		const results = []
		for ( let i = 0; i < list.length; i++) {
			//将序号索引存入缓存responses中，方便调用
			if ( this.responses[index] ) this.responses[index].index = i
			const obj = {...resource, ...{list: undefined, filter: undefined}}
			const item = await this.$object(deepClone(obj), list[i], taskId)
			results.push(
				Object.assign({}, item, {
					sourceId: this.source.id,
					sourceName: this.source.name
				})
			)
		}
		const result = resource.list ? results : (results[0] || null)
		return result
	}
	//处理规则对象
	async $object(obj, content, taskId) {
		return await this.traverseAndModify(obj, value => this.$text(value, content, taskId), content, taskId)
	}
	//遍历无限层级对象，并格式化数据
	async traverseAndModify(obj, modify, content, taskId) {
	    for (let key in obj) {
			if ( obj[key] ) {
				if (typeof obj[key] === 'object') {
					// 如果属性值是对象，则递归遍历
					if ( obj[key].list ) obj[key] = await this.$list(obj[key], content, taskId); //是否有list属性，有则建立新的数组遍历
					else await this.traverseAndModify(obj[key], modify, content, taskId)//否则递归遍历对象 
				} else obj[key] = await modify(obj[key]) // 调用修改函数
			}
	    }
		return obj
	}
	//处理规则文本
	async $text (rule, content, taskId) {
		rule = rule.toString()
		const newRule = await this.judgeFormat(rule, content, taskId)
		const pre = this.$pre(newRule)
		return statement.indexOf(pre) == -1 ? this.textFormat(newRule, content, taskId) : this.valueFormat(newRule, content, taskId)
	}
	//处理动态绑定的数据
	async dataHandle (data, params = {}) {
		let str = JSON.stringify(data)
		const binds = str.match(/{{[^}}]+}}/g) || []//找到双引号包裹的数据
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const baseUrl = this.baseUrl//基础链接
		for ( let i = 0; i < binds.length; i++ ) {
			const bind = binds[i]
			const handle = bind.match(/{{*([\s\S]*?)}}/)[1]
			try{
				//得到需要替换的值
				let reValue = typeof params[handle] != 'undefined' ? params[handle] : typeof globalData[handle] != 'undefined' ? globalData[handle] : eval('(' + (handle.includes('await') ? 'async ' : '') +'function () { return ' + handle + '})')
				//判断类型，同步函数直接执行，异步函数，需要同步执行，其它类型，直接返回
				reValue = typeof reValue == 'function' ? typeCall(reValue) == 'AsyncFunction' ? await reValue() : reValue() : reValue
				//如果是字符串\数字\布尔类型直接替换
				if ( ['string', 'number', 'boolean'].includes(typeof reValue) ) str = str.replace(bind, reValue)
				//非字符串类型需要连同双引号一起替换（这里不需要考虑双引号在字符串中间的情况比如：/request/{{myarg}}/index.html，这种情况下无法直接连同双引号一起替换，而且当替换数据非字符串\数字\布尔类型时，这种中间位置的变量去替换一定会报错）
				else str = str.replace('"' + bind + '"', JSON.stringify(reValue))
			}catch(e){
				//如果替换动态绑定函数出错，直接返回原值
				str = str.replace(bind, handle)
				this.appendLog({message: bind + '处理失败,' + e.toString(), type: 'error', by: 'dataHandle', result: e.toString()})
			}
		}
		return JSON.parse(str)
	}
	//处理静态数据中动态绑定的数据
	async staticDataHandle (staticData, params = {}) {
		const data = await this.dataHandle(staticData, params)
		const arr = typeCall(data) == 'Array' ? data : [data]
		const url = params.routeUrl
		const newArr = arr.map(item => Object.assign({}, { isAdult: this.source.isAdult || 0 }, item, {
				sourceId: this.source.id,
				sourceName: this.source.name,
				isAdult: this.source.isAdult || 0,
			}, url ? {url, uniqueId: url} : {})
		)
		return typeCall(data) == 'Array' ? newArr : newArr[0]
	}
	//处理请求中动态绑定的数据
	async requestHandle (request, params = {}) {
		const value = await this.dataHandle(request, params)
		return {...value, params1: params}
	}
	//获取唯一id
	getUniqueId (request) {
		return urlPrams(request.url || '', request.params || {})
	}
	//获取分享链接
	async getShareUrl (request) {
		const baseUrl = request.url.slice(0, 4) == 'http' ? '' : await this.getBaseUrl()
		return urlPrams((baseUrl + request.url) || '', request.params || {})
	}
	//获取基础域名
	async getBaseUrl () {
		try{
			const dynamic = this.source.dynamic
			if ( !dynamic ) return this.source.href
			const response = await this.$request(dynamic)
			const result = await this.$text(dynamic.result, response.data, response.taskId)
			//记录基础域名
			this.baseUrl = result
			return result
		}catch(e){
			this.appendLog({message: '获取基础域名失败,' + e.toString(), type: 'error', by: 'getBaseUrl', result: e.toString()})
			return this.source.href
		}
	}
	//判断条件
	async judgeFormat (rule, content, taskId) {
		try{
			const ifregex = new RegExp(/<if>*([\s\S]*?)<\/if>/, 'g');
			const elseifregex = new RegExp(/<elseif>*([\s\S]*?)<\/elseif>/, 'g');
			const elseregex = new RegExp(/<else(?!\w)>*([\s\S]*?)<\/else>/, 'g');
			const thenregex = new RegExp(/<then>*([\s\S]*?)<\/then>/, 'g');
			let match, result = ''
			const ifs = [], elseifs = [], thens = [], elses = []
			while ((match = ifregex.exec(rule))) {//匹配if标签
			  ifs.push({
				  index: match.index,
				  text: match[0]
			  });
			}
			while ((match = elseifregex.exec(rule))) {//匹配elseif标签
			  elseifs.push({
				  index: match.index,
				  text: match[0]
			  });
			}
			while ((match = elseregex.exec(rule))) {//匹配else标签
			  elses.push({
				  index: match.index,
				  text: match[0]
			  });
			}
			while ((match = thenregex.exec(rule))) {//匹配then标签
			  thens.push({
				  index: match.index,
				  text: match[0]
			  });
			}
			const arr = ifs.concat(elseifs).concat(elses).concat(thens).sort((a,b) => a.index - b.index)//合并标签并排序
			const newArr = []
			for ( let i = 0; i < arr.length; i++ ) {
				const text = arr[i].text
				if ( text.indexOf('<if>') > -1 ) newArr.push([])
				newArr[newArr.length-1].push(text)
			}
			for ( let i = 0; i < newArr.length; i++ ) {
				const items = newArr[i]
				for ( let j = 0; j < items.length; j++ ) {
					if ( items[j].indexOf('<if>') > -1 ) {
						const text = items[j].match(/<if>*([\s\S]*?)<\/if>/)[1]
						if ( await this.valueFormat(text, content, taskId) ) {
							result += items[j+1].indexOf('<then>') > -1 ? items[j+1].match(/<then>*([\s\S]*?)<\/then>/)[1] : ''
							break
						}
					}
					if ( items[j].indexOf('<elseif>') > -1 ) {
						const text = items[j].match(/<elseif>*([\s\S]*?)<\/elseif>/)[1]
						if ( await this.valueFormat(text, content, taskId) ) {
							result += items[j+1].indexOf('<then>') > -1 ? items[j+1].match(/<then>*([\s\S]*?)<\/then>/)[1] : ''
							break
						}
					}
					if ( items[j].indexOf('<else>') > -1 ) {
						result += items[j].match(/<else>*([\s\S]*?)<\/else>/)[1]
						break
					}
				}
			}
			return newArr.length > 0 ? result : rule
		}catch(e){
			this.appendLog({message: '条件编译失败,' + e.toString(), type: 'error', by: 'judeg', result: e.toString()})
			return ''
		}
	}
	//文本规则处理
	async textFormat (rule, content, taskId) {
		// try{
		// 	let text = rule.replace('@text:', '')
		// 	let arr = text.match(/<js>*([\s\S]*?)<\/js>/g) || []
		// 	for ( let i=0;i<arr.length;i++ ) {
		// 		let value = arr[i].match(/<js>*([\s\S]*?)<\/js>/)[1]
		// 		text = text.replace(arr[i], await this.valueFormat(value, content, taskId))
		// 	}
		// 	text = escapeFormat(text) || ''
		// 	return text
		// }catch(e){
		// 	this.appendLog({message: '文本编译失败,' + e.toString(), type: 'error', by: 'text', result: e.toString()})
		// 	return ''
		// }
		try {
		  let text = rule.replace('@text:', '')
		  let arr = text.match(/<js>*([\s\S]*?)<\/js>/g) || []
		  // 并行处理所有匹配项
		  const replacements = await Promise.all(
		    arr.map(async (item) => {
		      let value = item.match(/<js>*([\s\S]*?)<\/js>/)[1]
		      return {
		        original: item,
		        replacement: await this.valueFormat(value, content, taskId)
		      }
		    })
		  )
		  // 一次性替换所有内容
		  replacements.forEach(({ original, replacement }) => {
		    text = text.replace(original, replacement)
		  })
		  text = escapeFormat(text) || ''
		  return text
		} catch(e) {
		  this.appendLog({message: '文本编译失败,' + e.toString(), type: 'error', by: 'text', result: e.toString()})
		  return ''
		}
	}
	//处理数据,并发限制
	// async valueFormat(rules, content, taskId) {
	//   return this.formatLimiter.execute(() => this._valueFormat(rules, content, taskId));
	// }
	//处理数据
	async valueFormat (rules, content, taskId) {
		let result = content
		const arr = this.$extract(rules, statement)
		for ( let i = 0; i < arr.length; i++ ) {
			const rule = escapeFormat(arr[i].data)
			const pre = arr[i].type
			try{
				switch (pre) {
				  case '@response:':
				    const index = this.responses.findIndex(re => re.taskId == taskId);
				    result = index > -1 ? this.responses[index].content : '';
				    break;
				    
				  case '@sync:':
				    result = this.syncFormat(result, rule, taskId);
				    break;
				    
				  case '@async:':
				    result = await this.asyncFormat(result, rule, taskId);
				    break;
				    
				  case '@json:':
				    result = this.jsonFormat(result, rule);
				    break;
				    
				  case '@sel:':
				    result = this.selFormat(result, rule);
				    break;
				    
				  case '@for:':
				    result = this.forFormat(result, rule, taskId);
				    break;
				    
				  case '@map:':
				    result = this.mapFormat(result, rule, taskId);
				    break;
				    
				  case '@filter:':
				    result = this.filterFormat(result, rule, taskId);
				    break;
				    
				  case '@replace:':
				    result = this.replaceFormat(result, rule);
				    break;
				    
				  case '@replaceAll:':
				    result = this.replaceAllFormat(result, rule);
				    break;
				    
				  case '@plain:':
				    result = this.plainFormat(result, rule);
				    break;
				    
				  case '@split:':
				    result = this.splitFormat(result, rule);
				    break;
				    
				  case '@find:':
				    result = this.findFormat(result, rule, taskId);
				    break;
				    
				  case '@sort:':
				    result = this.sortFormat(result, rule, taskId);
				    break;
				    
				  case '@unique:':
				    result = this.uniqueFormat(result, rule, taskId);
				    break;
				    
				  case '@trim:':
				    result = this.trimFormat(result, rule);
				    break;
				    
				  case '@have:':
				    result = this.haveFormat(result, rule);
				    break;
				    
				  case '@unhave:':
				    result = this.unhaveFormat(result, rule);
				    break;
				    
				  case '@equal:':
				    result = this.equalFormat(result, rule);
				    break;
				    
				  case '@unequal:':
				    result = this.unequalFormat(result, rule);
				    break;
				    
				  case '@eval:':
				    result = this.evalFormat(result, rule);
				    break;
				    
				  case '@query:':
				    result = this.queryFormat(result, rule);
				    break;
				    
				  case '@match:':
				    result = this.matchFormat(result, rule);
				    break;
				    
				  case '@string:':
				    result = this.stringFormat(result, rule);
				    break;
				    
				  case '@dateFormat:':
				    result = this.dateFormatFormat(result, rule);
				    break;
				    
				  case '@join:':
				    result = this.joinFormat(result, rule);
				    break;
				    
				  case '@slice:':
				    result = this.sliceFormat(result, rule);
				    break;
				    
				  case '@reverse:':
				    result = this.reverseFormat(result, rule);
				    break;
				    
				  case '@encodeUri:':
				    result = this.encodeUriFormat(result, rule);
				    break;
				    
				  case '@decodeUri:':
				    result = this.decodeUriFormat(result, rule);
				    break;
				    
				  case '@objectKeys:':
				    result = this.objectKeysFormat(result, rule);
				    break;
				    
				  case '@objectArray:':
				    result = this.objectArrayFormat(result, rule);
				    break;
				    
				  case '@upper:':
				    result = this.upperFormat(result, rule);
				    break;
				    
				  case '@lower:':
				    result = this.lowerFormat(result, rule);
				    break;
				    
				  case '@console:':
				    const lastPre = arr[i-1]?.type || '';
				    this.appendLog({message: rule + ',' + JSON.stringify(result), by: lastPre, result});
				    break;
				    
				  default:
				    result = result + pre + rule;
				}
			}catch(e){
				result = ['have', 'unhave', 'equal', 'unequal'].includes(pre) ? false : ''
				const nextPre = arr[i+1]?.type || ''
				const value = typeCall(e) == 'Object' ? JSON.stringify(e) : e.toString()
				if ( nextPre == '@console:' ) this.appendLog({message: rule + ',' + value, by: pre, result: value, type: 'error'})
			}
		}
		return result
	}
	
	//同步方法处理
	syncFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const baseUrl = this.baseUrl
		//打印日志方法
		const uConsole = (text, content, type) => {
			this.appendLog({message: text + (content ? ',' : '') + (content || ''), type, by: 'syncFormat', result: content})
		}
		const handle = eval(
			"( function () {" + rule + " } )"
		)
		const res = handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//异步方法处理
	async asyncFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const globalMethod = deepClone(this.source.globalMethod || {})//自定义公共方法
		Object.keys(globalMethod).forEach(key => {
			const rule = globalMethod[key]
			globalMethod[key] = (arg) => { return this.$text(rule, arg) }
		})
		const baseUrl = this.baseUrl
		//打印日志方法
		const uConsole = (text, content, type) => {
			this.appendLog({message: text + (content ? ',' : '') + (content || ''), type, by: 'asyncFormat', result: content})
		}
		const handle = eval(
			"( async function () {" + rule + "} )"
		)
		const res = await handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//json数据处理
	jsonFormat (lastResult, rule) {
		let result = lastResult
		const arr = rule.split('.')
		for ( let i = 0;i < arr.length;i++ ) {
			const downIndex = arr[i].match(/\[([^\]]+)\]/)
			const key = downIndex ? arr[i].replace(downIndex[0], '') : arr[i]
			result = key ? result[key] : result
			result = downIndex ? result[downIndex[1]] : result
		}
		return result
	}
	//html数据处理
	selFormat (lastResult, rule) {
		const ands = rule.split('&&')//看看规则是否需要分开处理最后合并（不管结果如何，最后都会合并为一个数组），使用&&符号拼接
		let values = []
		ands.forEach(and => {//分开处理
			const arr = and.split('>>')//分割操作步骤 >>符号代表进行下一步操作
			let index = 0//操作步数
			let contents = typeCall(lastResult) == 'Array' ? lastResult : [lastResult]//判断处理内容是否是数组
			let isArray = typeCall(lastResult) == 'Array'//如果传入的处理内容是数组则代表返回数据默认是数组
			while ( index < arr.length ) {//开始处理
				let results = []
				const attrs = arr[index].split('.')
				const selectorName = attrs[1] ? attrs[1].split('[')[0] : null//选择器名称
				const downIndexs = attrs[1] ? attrs[1].match(/\[([^\]]+)\]/g) : null//获取中括号
				let attrSelector, downIndex
				//遍历中括号
				if ( downIndexs ) {
					downIndexs.forEach((d, k) => {
						const c = d.match(/\[([^\]]+)\]/)[1]//获取中括号包裹的内容
						//如果获取到了内容
						if ( c ) {
							//如果中括号内容是纯数字，这是定位数组的下标
							if ( /^\d+$/.test(c) ) downIndex = c
							//如果不是纯数字则是标签属性选择器
							else attrSelector = c
						}
						
					})
				}
				contents.forEach(content => {
					const document = typeCall(content) == 'String' ? new HTMLParser(content.trim()) : new HTMLParser(content.outerHTML.trim())
					if ( attrs[0] == 'class' ) {
						let result = document.getElementsByClassName(selectorName)
						//如果有标签属性匹配，去执行标签属性匹配
						if ( attrSelector ) result = getAttrSelector(attrSelector, result)
						//如果有数组下标，需要返回该数组索引的内容
						if ( downIndex ) results.push(result[downIndex])
						else {
							results = results.concat(result),
							isArray = true
						}
					} else if ( attrs[0] == 'id' ) {
						results = results.concat(document.getElementById(selectorName))
					} else if ( attrs[0] == 'tag' ) {
						let result = document.getElementsByTagName(selectorName)
						//如果有标签属性匹配，去执行标签属性匹配
						if ( attrSelector ) result = getAttrSelector(attrSelector, result)
						//如果有数组下标，需要返回该数组索引的内容
						if ( downIndex ) results.push(result[downIndex])
						else {
							results = results.concat(result),
							isArray = true
						}
					} else if ( attrs[0] == 'attr' ) {
						results.push(content.attributes[selectorName])
					} else if ( attrs[0] == 'html' ) {
						results.push(content.outerHTML.trim())
					} else if ( attrs[0] == 'content' ) {
						results.push(content.innerHTML.trim())
					} else if ( attrs[0] == 'text' ) {
						results.push(content.innerHTML.trim().replace(/<[^>]+>/g, '').trim().replace(/\s/g, '').replace(/\n/g, ' '))
					}
				})
				contents = results
				index++
			}
			values.push(isArray ? contents : contents.length > 1 ? contents : (contents[0] || ''))
		})
		if ( values.length > 1 ) {
			let arr = []
			values.forEach(val => {
				arr = arr.concat(val)
			})
			return arr
		} else return values[0]
	}
	
	// selFormat (lastResult, rule) {//html数据处理
	// 	try{
	// 		const arr = rule.split('>>')
	// 		let index = 0
	// 		let contents = typeCall(lastResult) == 'Array' ? lastResult : [lastResult]
	// 		let isArray = typeCall(lastResult) == 'Array'//返回数据是否是数组
	// 		while ( index < arr.length ) {
	// 			const ands = arr[index].split('&&')//是否查询多个指定元素
	// 			let results = []
	// 			ands.forEach(and => {
	// 				const attrs = and.split('.')
	// 				const downIndex = attrs[1] ? attrs[1].match(/\[([^\]]+)\]/) : null
	// 				contents.forEach(content => {
	// 					const document = typeCall(content) == 'String' ? new HTMLParser(content.trim()) : new HTMLParser(content.outerHTML.trim())
	// 					if ( attrs[0] == 'class' ) {
	// 						if ( downIndex ) results.push(document.getElementsByClassName(attrs[1].replace(downIndex[0], '') )[downIndex[1]])
	// 						else {
	// 							results = results.concat(document.getElementsByClassName(attrs[1])),
	// 							isArray = true
	// 						}
	// 					} else if ( attrs[0] == 'id' ) {
	// 						results = results.concat(document.getElementById(attrs[1]))
	// 					} else if ( attrs[0] == 'tag' ) {
	// 						if ( downIndex ) results.push(document.getElementsByTagName(attrs[1].replace(downIndex[0], '') )[downIndex[1]])
	// 						else {
	// 							results = results.concat(document.getElementsByTagName(attrs[1]))
	// 							isArray = true
	// 						}
	// 					} else if ( attrs[0] == 'attr' ) {
	// 						results.push(content.attributes[attrs[1]])
	// 					} else if ( attrs[0] == 'html' ) {
	// 						results.push(content.outerHTML.trim())
	// 					} else if ( attrs[0] == 'content' ) {
	// 						results.push(content.innerHTML.trim())
	// 					} else if ( attrs[0] == 'text' ) {
	// 						results.push(content.innerHTML.trim().replace(/<[^>]+>/g, '').trim().replace(/\s/g, '').replace(/\n/g, ' '))
	// 					}
	// 				})
	// 			})
	// 			contents = results
	// 			index++
	// 		}
	// 		return isArray ? contents : contents.length > 1 ? contents : (contents[0] || '')
	// 	}catch(e){
	// 		this.log('编译 ' + rule + ' 执行选择器函数失败：' + e.toString())
	// 		return ''
	// 	}
	// 	// try{
	// 	// 	let result = lastResult
	// 	// 	let arr = rule.split('>>')
	// 	// 	for ( let i =0; i < arr.length; i++ ) {
	// 	// 		const attrs = arr[i].split('.')
	// 	// 		const downIndex = attrs[1] ? attrs[1].match(/\[([^\]]+)\]/) : null
	// 	// 		result = typeCall(result) == 'Array' ? result[0] : result
	// 	// 		const document = typeCall(result) == 'String' ? new HTMLParser(result.trim()) : new HTMLParser(result.outerHTML.trim())
	// 	// 		if ( attrs[0] == 'class' ) {
	// 	// 			result = downIndex ? document.getElementsByClassName(attrs[1].replace(downIndex[0], '') )[downIndex[1]] : document.getElementsByClassName(attrs[1])
	// 	// 		} else if ( attrs[0] == 'id' ) {
	// 	// 			result = document.getElementById(attrs[1])
	// 	// 		} else if ( attrs[0] == 'tag' ) {
	// 	// 			result = downIndex ? document.getElementsByTagName(attrs[1].replace(downIndex[0], '') )[downIndex[1]] : document.getElementsByTagName(attrs[1])
	// 	// 		} else if ( attrs[0] == 'attr' ) {
	// 	// 			result = result.attributes[attrs[1]]
	// 	// 		} else if ( attrs[0] == 'html' ) {
	// 	// 			result = result.outerHTML.trim()
	// 	// 		} else if ( attrs[0] == 'content' ) {
	// 	// 			result = result.innerHTML.trim()
	// 	// 		} else if ( attrs[0] == 'text' ) {
	// 	// 			result = result.innerHTML.trim()
	// 	// 			result = result.replace(/<[^>]+>/g, '').trim()
	// 	// 		}
	// 	// 	}
	// 	// 	return result
	// 	// }catch(e){
	// 	// 	console.log('e', e);
	// 	// 	this.throwerror(rule, e)
	// 	// 	return ''
	// 	// }
	// }
	//for循环
	forFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const baseUrl = this.baseUrl
		const handle = eval("( function () { var result = []; lastResult.forEach((item, key) => {" + rule +  "}); return result } )")
		const res = handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//map循环
	mapFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const baseUrl = this.baseUrl
		const handle = eval("( function () { return lastResult.map((item, key) =>" + rule +  ") } )")
		const res = handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//列表筛选
	filterFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		const globalData = this.source.globalData || {}
		const baseUrl = this.baseUrl
		const handle = eval("( function () { return lastResult.filter((item, key) =>" + rule +  ") } )")
		const res = handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//替换单个字符串
	replaceFormat (lastResult, rule) {
		const arr = rule.split('>>')
		if ( arr.length > 0 ) {
			return lastResult.replace(arr[0], arr[1] || '')
		} else { return lastResult }
	}
	//替换全部字符串
	replaceAllFormat (lastResult, rule) {
		const arr = rule.split('>>')
		if ( arr.length > 0 ) {
			return lastResult.replace(eval(`/${arr[0]}/g`), arr[1] || '')
		} else { return lastResult }
	}
	//去除标签，换行符，空格
	plainFormat (lastResult, rule) {
		return lastResult.trim().replace(/<[^>]+>/g, '').trim().replace(/\s/g, '').replace(/\n/g, ' ')
	}
	//split处理
	splitFormat (lastResult, rule) {
		const arr = rule.split('>>')
		const split = arr[0]
		return arr[1] ? lastResult.split(split)[arr[1]] : lastResult.split(split)
	}
	//查找满足条件的一项
	findFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const baseUrl = this.baseUrl
		const handle = eval("( function () { var index = lastResult.findIndex((item, key) =>" + rule +  "); return index > -1 ? lastResult[index] : '' } )")
		const res = handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//排序
	sortFormat (lastResult, rule, taskId) {
		//获取原始请求结果
		const index = this.responses.findIndex(re => re.taskId == taskId)
		const response = index > -1 ? this.responses[index] : {}
		//设置globalData可编辑
		let globalData = this.source.globalData || {}//自定义公共变量
		const baseUrl = this.baseUrl
		const handle = eval("( function () { lastResult.sort((a, b) =>" + rule +  "); return lastResult } )")
		const res = handle()
		//记录可能被改变的globalData属性
		this.source.globalData = globalData
		return res
	}
	//去重
	uniqueFormat (lastResult, rule) {//排序
		const attr = rule ? 'item[' + rule +  ']' : 'item'
		const handle = eval(`( function () { var result = []; lastResult.forEach((item, key) => { if ( !result.includes(${attr}) ) result.push(item) }); return result } )`)
		const res = handle()
		return res
	}
	//去除2边的空格
	trimFormat (lastResult, rule) {
		return lastResult.trim()
	}
	//是否包含某个字符串
	haveFormat (lastResult, rule) {
		return lastResult.indexOf(rule) > -1
	}
	//是否不包含某个字符串
	unhaveFormat (lastResult, rule) {
		return lastResult.indexOf(rule) == -1
	}
	//是否相等
	equalFormat (lastResult, rule) {
		return lastResult == rule
	}
	//是否不相等
	unequalFormat (lastResult, rule) {
		return lastResult != rule
	}
	//字符串转函数
	evalFormat (lastResult, rule) {
		const handle = rule == 'function' ? eval( "( function () { return " + lastResult + "} )" ) : eval( "(" + lastResult + ")" )
		return typeof handle == "function" ? handle() : handle
	}
	//url链接参数处理
	queryFormat (lastResult, rule) {
		const index = lastResult.indexOf('?')
		const search = index > -1 ? lastResult.substring(index+1) : lastResult
		const vars = search.split('&')
		const obj = {}
		for (let i=0;i<vars.length;i++) {
		    let pair = vars[i].split("=");
			obj[pair[0]] = pair[1]
		}
		return obj;
	}
	//正则匹配2个指定字符之间
	matchFormat (lastResult, rule) {
		const arr = rule.split('>>')
		const match = lastResult.match(eval(`/${arr[0]}*([\\s\\S]*?)${arr[1]}/`) )
		return arr[2] ? match[arr[2]] : match
	}
	//toString函数
	stringFormat (lastResult, rule) {
		let binary = rule || 10
		return lastResult.toString(binary)
	}
	//日期格式化
	dateFormatFormat (lastResult, rule) {
		const formats = rule || 'yyyy-mm-dd hh:mm:ss'
		return dateFormat(lastResult, formats)
	}
	//join函数
	joinFormat (lastResult, rule) {
		const binary = rule || ''
		return lastResult.join(binary)
	}
	//切割函数
	sliceFormat (lastResult, rule) {
		const arr = rule.split('>>')
		return arr.length > 1 ? lastResult.slice(arr[0], arr[1]) : lastResult.slice(arr[0])
	}
	//取反函数
	reverseFormat (lastResult, rule) {
		return lastResult.reverse()
	}
	//转义函数
	encodeUriFormat (lastResult, rule) {
		return encodeURIComponent(JSON.stringify(lastResult))
	}
	//解除转义函数
	decodeUriFormat (lastResult, rule) {
		return JSON.parse(decodeURIComponent(lastResult))
	}
	//将对象key转化为数组函数
	objectKeysFormat (lastResult, rule) {
		return Object.keys(lastResult)
	}
	//将对象值转化为数组函数
	objectArrayFormat (lastResult, rule) {
		const keys = Object.keys(lastResult)
		let arr = []
		keys.forEach(key => arr = arr.concat(lastResult[key]))
		return arr
	}
	//转大写字母
	upperFormat (lastResult, rule) {
		return lastResult.toUpperCase()
	}
	//转小写字母
	lowerFormat (lastResult, rule) {
		return lastResult.toLowerCase()
	}
}

//转义html特殊字符
function htmlDecode (str) {
	let s = ""
	if (str.length == 0) return ""
	// s = str.replace(/&#x/g, "\\u")
	s = str.replace(/&#58;/g, ":")
	s = s.replace(/&#32;/g, " ")
	s = s.replace(/&#33;/g, "!")
	s = s.replace(/&#34;/g, '"')
	s = s.replace(/&#35;/g, "#")
	s = s.replace(/&#36;/g, "$")
	s = s.replace(/&#37;/g, "%")
	s = s.replace(/&#38;/g, "&")
	s = s.replace(/&#39;/g, "'")
	s = s.replace(/&#40;/g, "(")
	s = s.replace(/&#41;/g, ")")
	s = s.replace(/&#42;/g, "*")
	s = s.replace(/&#43;/g, "+")
	s = s.replace(/&#44;/g, ",")
	s = s.replace(/&#45;/g, "-")
	s = s.replace(/&#46;/g, ".")
	s = s.replace(/&#47;/g, "/")
	s = s.replace(/&#13;/g, "\n")
	s = s.replace(/<br\/>/g, "\n")
	s = s.replace(/<br \/>/g, "\n")
	s = s.replace(/<br>/g, "\n")
	s = s.replace(/&amp;/g, "")
	s = s.replace(/&nbsp;/g, " ")
	s = s.replace(/&#8211;/g, "–")
	s = s.replace(/#65279;/g, "")
	s = s.replace(/&hellip;/g, '...')
	s = s.replace(/&mdash;/g, '—')
	s = s.replace(/&ldquo;/g, "“")
	s = s.replace(/&rdquo;/g, "”")
	s = s.replace(/&middot;/g, ".")
	s = s.replace(/&iexcl;/g, "¡")
	s = s.replace(/&cent;/g, "¢")
	s = s.replace(/&pound;/g, "£")
	s = s.replace(/&curren;/g, "¤")
	s = s.replace(/&yen;/g, "¥")
	s = s.replace(/&sect;/g, "§")
	s = s.replace(/&uml;/g, "¨")
	s = s.replace(/&copy;/g, "©")
	s = s.replace(/&ordf;/g, "ª")
	s = s.replace(/&laquo;/g, "«")
	s = s.replace(/&not;/g, "¬")
	s = s.replace(/&reg;/g, "®")
	s = s.replace(/&macr;/g, "¯")
	s = s.replace(/&deg;/g, "°")
	s = s.replace(/&plusmn;/g, "±")
	s = s.replace(/&acute;/g, "´")
	s = s.replace(/&micro;/g, "µ")
	s = s.replace(/&para;/g, "¶")
	s = s.replace(/&middot;/g, "·")
	s = s.replace(/&cedil;/g, "¸")
	s = s.replace(/&ordm;/g, "º")
	s = s.replace(/&raquo;/g, "»")
	s = s.replace(/&iquest;/g, "¿")
	s = s.replace(/&Agrave;/g, "À")
	s = s.replace(/&Aacute;/g, "Á")
	s = s.replace(/&Acirc;/g, "Â")
	s = s.replace(/&Atilde;/g, "Ã")
	s = s.replace(/&Auml;/g, "Ä")
	s = s.replace(/&Aring;/g, "Å")
	s = s.replace(/&AElig;/g, "Æ")
	s = s.replace(/&Ccedil;/g, 'Ç')
	s = s.replace(/&Egrave;/g, "È")
	s = s.replace(/&Eacute;/g, "É")
	s = s.replace(/&Ecirc;/g, "Ê")
	s = s.replace(/&Euml;/g, "Ë")
	s = s.replace(/&Igrave;/g, "Ì")
	s = s.replace(/&Iacute;/g, "Í")
	s = s.replace(/&Icirc;/g, "Î")
	s = s.replace(/&Iuml;/g, "Ï")
	s = s.replace(/&Ntilde;/g, "Ñ")
	s = s.replace(/&Ograve;/g, "Ò")
	s = s.replace(/&Oacute;/g, "Ó")
	s = s.replace(/&Ocirc;/g, "Ô")
	s = s.replace(/&Otilde;/g, "Õ")
	s = s.replace(/&Ouml;/g, "Ö")
	s = s.replace(/&Oslash;/g, "Ø")
	s = s.replace(/&Ugrave;/g, "Ù")
	s = s.replace(/&Uacute;/g, "Ú")
	s = s.replace(/&Ucirc;/g, "Û")
	s = s.replace(/&Uuml;/g, "Ü")
	s = s.replace(/&szlig;/g, "ß")
	s = s.replace(/&agrave;/g, "à")
	s = s.replace(/&aacute;/g, "á")
	s = s.replace(/&acirc;/g, "â")
	s = s.replace(/&atilde;/g, "ã")
	s = s.replace(/&auml;/g, "ä")
	s = s.replace(/&aring;/g, "å")
	s = s.replace(/&aelig;/g, "æ")
	s = s.replace(/&ccedil;/g, "ç")
	s = s.replace(/&egrave;/g, "è")
	s = s.replace(/&eacute;/g, "é")
	s = s.replace(/&ecirc;/g, "ê")
	s = s.replace(/&euml;/g, "ë")
	s = s.replace(/&igrave;/g, "ì")
	s = s.replace(/&iacute;/g, "í")
	s = s.replace(/&icirc;/g, "î")
	s = s.replace(/&iuml;/g, "ï")
	s = s.replace(/&ntilde;/g, "ñ")
	s = s.replace(/&ograve;/g, "ò")
	s = s.replace(/&oacute;/g, "ó")
	s = s.replace(/&ocirc;/g, "ô")
	s = s.replace(/&otilde;/g, "õ")
	s = s.replace(/&ouml;/g, "ö")
	s = s.replace(/&divide;/g, "÷")
	s = s.replace(/&oslash;/g, "ø")
	s = s.replace(/&ugrave;/g, "ù")
	s = s.replace(/&uacute;/g, "ú")
	s = s.replace(/&ucirc;/g, "û")
	s = s.replace(/&uuml;/g, "ü")
	s = s.replace(/&yuml;/g, "ÿ")
	return s
}

//转义字符替换
export function escapeFormat (str) {
	str = str.replace(/&es;/g, '\\')//转义斜杠
	str = str.replace(/&wrap;/g, '\n')//换行符
	str = str.replace(/&retn;/g, '\r')//回车符
	str = str.replace(/&nbsp;/g, ' ')//空格符
	str = str.replace(/&tabu;/g, '\t')//制表符
	str = str.replace(/&quot;/g, '\"')//双引号
	str = str.replace(/&apos;/g, "\'")//单引号
	str = str.replace(/&lt;/g, "<")//左尖括号
	str = str.replace(/&gt;/g, ">")//右尖括号
	str = str.replace(/&oe;/g, "{")//左大括号
	str = str.replace(/&ce;/g, "}")//右大括号
	return str
}

//匹配定义符和内容
export function extractPattern (inputString, patterns) {
	const extractedData = [];
	const patternsSync = inputString.match(eval(`/${patterns.join('|')}/g`)) || []
	patternsSync.forEach((pattern, key) => {
		let matches
	    const regex = new RegExp(`(${pattern})([\\s\\S]*?)(${patterns.join('|')}|$)`);
		const match = regex.exec(inputString)
		if ( match ) {
			matches = { type: match[1], data: match[2] }
			extractedData.push(matches);
			inputString = inputString.replace(matches.type + matches.data, '')
		}
	});
	return extractedData
}

export async function getClassification (req, rule) {
	const list = []
	const arr = rule.split('$:')
	for ( let i = 0; i < arr.length; i++ ) {
		const item = arr[i]
		let child = []
		const title = item.split('?:').length > 1 ? item.split('?:')[0] : ''
		const urlRule = item.split('?:').length > 1 ? item.split('?:')[1] : item
		const preIndex = urlRule.indexOf(':')
		const pre = urlRule.slice(0, preIndex+1)
		if ( pre == '@sync:' ) {
			try{
				const handle = eval('( function () {' + escapeFormat(urlRule.replace(pre, '')) + '} )')
				child = handle() || []
			}catch(e){
				child = []
				uni.showToast({
					title: e.toString(),
					icon:'none',
					duration: 5000
				})
			}
		} else if ( pre == '@async:' ) {
			try{
				const handle = eval('( async function () {' + escapeFormat(urlRule.replace(pre, '')) + '} )')
				child = await handle() || []
			}catch(e){
				child = []
				uni.showToast({
					title: e.toString(),
					icon:'none',
					duration: 5000
				})
			}
		} else {
			let urls = urlRule.split('+:')
			urls.forEach(url => {
				child.push({
					label: url.split('::')[0],
					value: url.split('::')[1]
				})
			})
		}
		list.push({
			title: title,
			child,
			req: req
		})
	}
	return list
}

//并发限制
class ConcurrentLimiter {
  constructor(maxConcurrent = 3) {
    this.maxConcurrent = maxConcurrent;
    this.current = 0;
    this.queue = [];
  }

  async execute(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject });
      this._process();
    });
  }
  
  clear() {
	this.queue = [];
  }

  _process() {
    if (this.current >= this.maxConcurrent || this.queue.length === 0) {
      return;
    }

    this.current++;
    const { task, resolve, reject } = this.queue.shift();

    Promise.resolve(task())
      .then(resolve)
      .catch(reject)
      .finally(() => {
        this.current--;
        this._process();
      });
  }
}

//webview请求方式类
class WvReq {
	constructor (config, header, id) {
		this.config = config
		this.header = header
		this.id = id
		this.wv = null
		
		this._timeoutTimer = null
		this._delayTimer = null
		this._content = null
		this._error = null
		this._time = null
		
		this._event = {}
		
		this.init()
	}
	init () {
		this.wv = plus.webview.create(this.config.requestUrl, this.id, {
			additionalHttpHeaders: this.header,
			position: 'absolute',
			left: '0',
			left: '0',
			width: '0',
			height: '0',
			animationOptimization: 'none',
			cachemode: 'noCache',
			webviewBGTransparent: true,
			blockNetworkImage: true,//阻塞图片加载，加快网页加载速度
			bounce: 'none',
			disablePlus: false,
			hardwareAccelerated: false,
			opacity: 0,
			render: 'onscreen'
		})
		
		this.wv.onloading = () => {
			this._timeoutTimer = setTimeout(() => {
				this.evalJS()
			}, this.config.requestTimeout)
		}
		this.wv.onloaded = () => {
			this._time++
			if ( this._time == this.config.requestVisits ) {
				this._clearTimeoutTimer()
				this._delayTimer = setTimeout(() => {
					this.evalJS()
				}, this.config.requestDelay)
			}
		}
		this.wv.addEventListener('close', () => {
			this._clearTimeoutTimer()
			this._clearDelayTimer()
			this.wv = null
		}, false);
		this.wv.addEventListener('error', () => {
			this.close()
			this.emit('fail', {
				errCode: 3,
				errMsg: 'webview加载网页失败'
			})
		}, false);
	}
	//注入js
	evalJS () {
		this.wv && this.wv.evalJS(`
			try {
				plus.webview.postMessageToUniNView({    
				 content: document.documentElement.outerHTML,
				 webviewRequest: true
				}, '__uniapp__service');
			} catch (e) {
				plus.webview.postMessageToUniNView({
				  error: JSON.stringify(e),
				  webviewRequest: true
				}, '__uniapp__service');
			}
		`);
	}
	
	on (key, value) {
		this._event[key] = value
	}
	
	emit (name, data) {
		this._event[name] && this._event[name](data)
	}
	
	close () {
		plus.webview.close(this.wv)
		this._event = {}
	}
	
	_clearTimeoutTimer () {
		if ( this._timeoutTimer ) {
			clearTimeout(this._timeoutTimer)
			this._timeoutTimer = null
		}
	}
	
	_clearDelayTimer () {
		if ( this._delayTimer ) {
			clearTimeout(this._delayTimer)
			this._delayTimer = null
		}
	}
}
let wvReq, wvcResolveCall, wvcRejectCall
function getWebviewContent (config, header, id) {
	return new Promise((resolve, reject) => {
		wvcResolveCall = resolve
		wvcRejectCall = reject
		wvReq = new WvReq(config, header, id)
		wvReq.on('fail', function (e) {
			wvcRejectCall(e)
		})
	})
}

function webviewTaskabort (ids) {
	ids.forEach(id => {
		const wv = plus.webview.getWebviewById( id );
		plus.webview.close(wv)
	})
}

//标签属性选择器
function getAttrSelector (selector, tags) {
	//内容中包含$=符号，匹配属性值以$=符号后面字符串结尾的标签
	if ( selector.includes('$=') ) {
		const attrName = selector.split('$=')[0].trim()
		const attrValue = selector.split('$=')[1]
		tags = tags.filter(r => r.attributes[attrName] && r.attributes[attrName].endsWith(attrValue) )
	}
	//内容中包含^=符号，匹配属性值以^=符号后面字符串开头的标签
	else if ( selector.includes('^=') ) {
		const attrName = selector.split('^=')[0].trim()
		const attrValue = selector.split('^=')[1]
		tags = tags.filter(r => r.attributes[attrName] && r.attributes[attrName].startsWith(attrValue) )
	}
	//内容中包含!*=符号，匹配属性值不包含!*=符号后面字符串的标签
	else if ( selector.includes('!*=') ) {
		const attrName = selector.split('!*=')[0].trim()
		const attrValue = selector.split('!*=')[1]
		tags = tags.filter(r => r.attributes[attrName] && !r.attributes[attrName].includes(attrValue) )
	}
	//内容中包含*=符号，匹配属性值包含*=符号后面字符串的标签
	else if ( selector.includes('*=') ) {
		const attrName = selector.split('*=')[0].trim()
		const attrValue = selector.split('*=')[1]
		tags = tags.filter(r => r.attributes[attrName] && r.attributes[attrName].includes(attrValue) )
	}
	//内容中包含!~=符号，匹配属性值单词（空格分割）不包含!~=符号后面字符串的标签
	else if ( selector.includes('!~=') ) {
		const attrName = selector.split('!~=')[0].trim()
		const attrValue = selector.split('!~=')[1]
		tags = tags.filter(r => {
			var attr = r.attributes[attrName]
			attr = attr ? attr.split(' ') : []
			return !attr.includes(attrValue)
		})
	}
	//内容中包含~=符号，匹配属性值单词（空格分割）包含~=符号后面字符串的标签
	else if ( selector.includes('~=') ) {
		const attrName = selector.split('~=')[0].trim()
		const attrValue = selector.split('~=')[1]
		tags = tags.filter(r => {
			var attr = r.attributes[attrName]
			attr = attr ? attr.split(' ') : []
			return attr.includes(attrValue)
		} )
	}
	//内容中包含!=符号，匹配属性值不等于!=符号后面字符串的标签
	else if ( selector.includes('!=') ) {
		const attrName = selector.split('!=')[0].trim()
		const attrValue = selector.split('!=')[1]
		tags = tags.filter(r => r.attributes[attrName] && r.attributes[attrName] != attrValue )
	}
	//内容中包含=符号，匹配属性值等于=符号后面字符串的标签
	else if ( selector.includes('=') ) {
		const attrName = selector.split('=')[0].trim()
		const attrValue = selector.split('=')[1]
		tags = tags.filter(r => r.attributes[attrName] && r.attributes[attrName] == attrValue )
	}
	//内容中不包含任何等于符号，匹配拥有该属性值的标签
	else {
		tags = tags.filter(r => r.attributes[selector.trim()] )
	}
	return tags
}

/**
 * 传入js字符传到浏览器环境并执行
 * @param {String} jsCode 执行的js代码
*/
let ww, wwResolveCall = {}, wwRejectCall = {}
function WebWorker (jsCode) {
	return new Promise((resolve, reject) => {
		const id = randomId()
		wwResolveCall[id] = resolve
		wwRejectCall[id] = reject
		function evalCode () {
			ww.evalJS(`
			evalJS('${encodeURIComponent(JSON.stringify(jsCode))}', '${id}')
			`);
		}
		if ( !ww ) {
			ww = plus.webview.create('/hybrid/html/WebWorker.html', 'webworker', {
				position: 'absolute',
				left: '0',
				left: '0',
				width: '0',
				height: '0',
				animationOptimization: 'none',
				// cachemode: 'noCache',
				webviewBGTransparent: true,
				blockNetworkImage: false,//不阻塞图片加载
				bounce: 'none',
				disablePlus: false,
				hardwareAccelerated: false,
				opacity: 0,
				render: 'always'
			})
			ww.addEventListener('close', () => {
				ww = null
				wwResolveCall = {}
				wwRejectCall = {}
			}, false);
			ww.addEventListener('error', () => {
				plus.webview.close(ww)
				wwRejectCall[id]('加载浏览器环境失败')
			}, false);
			ww.onloaded = () => {
				evalCode()
			}
		} else {
			evalCode()
		}
	})
}

// plusMessage全局只能监听一次，不能多次监听，否则回调也会一直增多，也不能用plus.globalEvent.removeEventListener移除监听，会将全局的监听都给移除掉，会导致vue页面跳转和生命周期异常
plus.globalEvent.addEventListener('plusMessage', function (e) {
	//判断是否是webview请求方式的通信,因为vue页面貌似也会触发plusMessage
	if ( e.data.webviewRequest ) {
		wvReq.close()
		wvReq = null
		const data = e.data
		if ( data.content ) {
			wvcResolveCall({
				data: data.content,
				errCode: 0,
				errMsg: 'ok'
			})
		}
		if ( data.error ) {
			wvcRejectCall({
				errCode: 0,
				errMsg: data.error
			})
		}
		wvcResolveCall = null
		wvcRejectCall = null
	}
	//判断是否是将js代码传到webview运行的结果
	if ( e.data.webWorker ) {
		const data = e.data
		const id = data.webWorkerId
		if ( data.errCode == 0 ) {
			wwResolveCall[id](data.result)
			deleteProperty(wwResolveCall, id)
		} else {
			wwRejectCall[id](data.errMsg)
			deleteProperty(wwRejectCall, id)
		}
	}
}, false);
